package cpr.context;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

import cpr.Printer;
import cpr.Rule;
import cpr.Utils;
import cpr.context.ruleacceptor.GlobalRuleNrSetRuleAcceptor;
import cpr.data.HLVector;
import cpr.data.RuleComparable;
import cpr.data.SplitCluster;
import cpr.data.SplitClusterComparator;
import cpr.helper.ScoreHelper;
import data.IClusterDescriptor;

public class SelectContext {
	private final static String FILE_PREFIX_SELECT ="select";
	
	private Context ctx;
	public SelectContext(Context ctx){
		this.ctx = ctx;
	}
	
	public Context getContext(){
		return ctx;
	}
	public Map TO_DELETE_selectRulesFromHLFile(String filename, //<Integer, SplitCluster>
			HLVector vector, SplitCluster parent, boolean onlyDigital,
			int RULES_TO_LOAD) {
		try {
			Printer.print("score=" + vector.score + "="
					+ ScoreHelper.printHLVectorAsArray(vector) + "\n");
			if (parent != null) {
				Printer.print("parent: " + parent.getAccordingToRule());
			}

			Reader f = new FileReader(filename);
			BufferedReader br = new BufferedReader(f);
			String s = null;
			int cnt = 1;
//			Rule.totalRuleNr = 0;
			int ruleNr = 0;
			int ruleNrGlobal = 0;

			Map set = new HashMap(); 
			do {
				s = br.readLine();
				if (s != null) {
					Rule r = ctx.getIOContext().createFromHLDigitalString(s, onlyDigital, ++ruleNr, ++ruleNrGlobal);
					boolean parentOk = true;
					if (parent != null) {
						parentOk = false;
						int siparent = Utils.printRuleAccordingToVectorDigital(
								r, parent.aVector);
						if (siparent == parent.accordingToRule) {
							parentOk = true;
						}
					}

					if (parentOk) {
						int[] tmp = Utils.ruleValueFor(r, vector);
						int si = Utils.printRuleAccordingToVectorDigital(r,
								vector);
						SplitCluster c = (SplitCluster) set.get(Integer.valueOf(si));
						if (c == null) {
							c = new SplitCluster(r, vector);
						}
						c.setCounter(c.getCounter() + 1);
						set.put(Integer.valueOf(si), c);
						cnt++;

					}

				}
				if ((RULES_TO_LOAD > 0) && (cnt > RULES_TO_LOAD)) {
					s = null;
				}
			} while (s != null);
			f.close();

			return set;

		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return null;
	}

	
	public String getSelectFilePath(HLVector vector, IClusterDescriptor parent) {
		
		ScoreHelper helper = getContext().getScoreHelper();
		
		String scoreString = ".score_"+helper.printHLVectorAsArray(vector,"-");
		String scopeScoreString = "";
		String scopeRuleString = "";
		if (parent!=null) {
			scopeScoreString = "_scope-score_"+helper.printHLVectorAsArray(parent.getVector(),"-");
			scopeRuleString = "_scope-rule_"+ getContext().getPrintContext().createRuleDigitalDigest(parent.getRule(), parent.getVector(), "-");
		}
		
		String filename = getContext().getRawDataFilePath() + "."+FILE_PREFIX_SELECT+ scoreString+ scopeScoreString + scopeRuleString;
		
		return filename;
	}
	
	public Rule getRule(int globalRuleNr) {
		return null;
	}
	
    
    public void preloadAllSavedSelections() {
        String folder = getContext().getDefaultDataFolder();
        File file = new File(folder);
        if (!file.exists()) {
            return;
        }
        if (!file.isDirectory()) {
            return;
        }
        FilenameFilter ff = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                String st = getContext().getRawDataFilename()+"."+FILE_PREFIX_SELECT;
                if (name.startsWith(st)) return true;
                return false;
            }
        };
        File[] files = file.listFiles(ff);
        
        Set allRuleNrs = new HashSet();
        for (int i = 0; i < files.length; i++) {
            Set ruleNrs = loadSelection(files[i].getAbsolutePath(),null);
            allRuleNrs.addAll(ruleNrs);
        }
        
        GlobalRuleNrSetRuleAcceptor ruleAcceptor = new GlobalRuleNrSetRuleAcceptor(allRuleNrs);
        String csv_file  =  ctx.getPureDigitalDataFilePath();
        ruleAcceptor.setCacheRuleContext(ctx.getGlobalRuleCache());
        
        List hl_rules = ctx.getIOContext().readRulesFromPureDigitalFile(csv_file, ctx.getRulesToLoad(),ruleAcceptor);
        
        
    }
    
    
    public Set<SplitCluster> loadSelection(String filename, HLVector vector) {
        try {
            File file = new File(filename);
            if (!file.exists()) {
                return null;
            }
            Printer.println("Loading "+filename);
            Reader r = new FileReader(file);
            BufferedReader br = new BufferedReader(r);
            String s = null;
            Pattern spacePattern = Pattern.compile( "\\s" );
            
            Map map = new HashMap();
            do {
                s= br.readLine();
                if (s!=null) {
                    String[] st = spacePattern.split(s);
                    if (st.length!=2) {
                        Printer.println("format error: "+ s);
                    } else {
                        map.put(new Integer(st[0]),new Integer(st[1]));
                    }
                }
            } while(s!=null);
            br.close();
            
            //WorksetLoaderRuleAcceptor ruleAcceptor = new WorksetLoaderRuleAcceptor(ruleNrs);
            
            GlobalRuleNrSetRuleAcceptor ruleAcceptor = new GlobalRuleNrSetRuleAcceptor(map.keySet());
            String csv_file  =  ctx.getPureDigitalDataFilePath();
            ruleAcceptor.setCacheRuleContext(ctx.getGlobalRuleCache());
            
            if (vector==null) {
                return map.keySet();
            }
            List hl_rules = ctx.getIOContext().readRulesFromPureDigitalFile(csv_file, ctx.getRulesToLoad(),ruleAcceptor);
            
            //List hl_rules = ctx.getIOContext().readRulesFromPureDigitalFile(csv_file, ctx.getRulesToLoad(),ruleAcceptor);
            
//          hl_rules = Utils.readRulesFromHLFile(
//                  getCommandContext().getContext().getDigitalDataFilePath(), -1,false, ruleAcceptor);
            Set<SplitCluster> set = new HashSet<SplitCluster>();
            
            for (Iterator iter = hl_rules.iterator(); iter.hasNext();) {
                Rule aRule = (Rule) iter.next();
                SplitCluster c = new SplitCluster(aRule, vector);
                Integer cnt = (Integer)map.get(aRule.ruleNrGlobal);
                c.setCounter(cnt.intValue());
                set.add(c);
            }
            
            br.close();
            
            return set;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
	public Set<SplitCluster> loadSelection(HLVector vector, IClusterDescriptor parent) {
        String filename = getSelectFilePath(vector, parent);
        return loadSelection(filename,vector);
	}
	
	public void saveSelection(Set<SplitCluster> clusterSet, HLVector vector, IClusterDescriptor parent) {
		//file qube_rule_1_score_45056.select.score_3-3-3-3-3_scope-score_2-3-3-3-0_scope-rule_3404-655-6434-45534-554
		ScoreHelper helper = getContext().getScoreHelper();
		
		String scoreString = ".score_"+helper.printHLVectorAsArray(vector,"-");
		String scopeScoreString = "";
		String scopeRuleString = "";
		if (parent!=null) {
			scopeScoreString = "_scope-score_"+helper.printHLVectorAsArray(parent.getVector(),"-");
			scopeRuleString = "_scope-rule_"+ getContext().getPrintContext().createRuleDigitalDigest(parent.getRule(), parent.getVector(), "-");
		}
		
		String filename = getSelectFilePath(vector, parent);
		
	
		try {
        	Printer.println("Saving "+filename);
    		
        	File file = new File(filename);
    		if (file.exists()) {
    			file.delete();
    		}
    		
    		Writer w = new FileWriter(file);
    		BufferedWriter bw = new BufferedWriter(w);
    		StringBuffer fileOut = new StringBuffer();
            
    		for (Iterator iter = clusterSet.iterator(); iter.hasNext();) {
                SplitCluster c =  (SplitCluster)iter.next();
//                Printer.println(c.aRule.ruleNrGlobal+". " +getContext().getPrintContext().printRuleUsingDict(c.aRule, c.aVector)+" x "+ c.getCounter());
                fileOut.append(c.aRule.ruleNrGlobal+"\t"+c.getCounter()+"\n");
    		}
    		bw.write(fileOut.toString());
    		bw.close();
    	} catch (Exception ex) {
    		throw new RuntimeException(ex);
    	}
	}
	
	public Set<SplitCluster> selectSplitClusterSet(HLVector vector, IClusterDescriptor parentCluster) {
		Set<SplitCluster> loaded=  loadSelection(vector, parentCluster);
		if (loaded!=null) {
			TreeSet<SplitCluster> ts = new TreeSet<SplitCluster>(new SplitClusterComparator());
	        ts.addAll(loaded);
			return ts;
		}
		
		Map<RuleComparable, SplitCluster>  map = selectRulesFromHLFilePureDigital(vector,parentCluster);
        TreeSet<SplitCluster> ts = new TreeSet<SplitCluster>(new SplitClusterComparator());
        ts.addAll(map.values());
        saveSelection(ts, vector, parentCluster);
		
        return ts;
	}
	static class SplitClusterAdapter implements IClusterDescriptor {
		private SplitCluster cluster;
		public static SplitClusterAdapter create(SplitCluster sc) {
			if (sc==null) { return null;}
			return new SplitClusterAdapter(sc);
		}
		public SplitClusterAdapter(SplitCluster sc) {
			cluster = sc;
		}
		public Rule getRule() {
			return cluster.aRule;
		}
		public HLVector getVector() {
			return cluster.aVector;
		}
		public int getSize() {
			return -1;
		}
		
	}
	public Set<SplitCluster> selectSplitClusterSet(HLVector vector, SplitCluster parentCluster) {
		
		return selectSplitClusterSet(vector, SplitClusterAdapter.create(parentCluster));
	}
	
	private Map<RuleComparable, SplitCluster> selectRulesFromHLFilePureDigital(
			HLVector vector, IClusterDescriptor parent) {
		try {
			Printer.print("score=" + vector.score + "="
					+ ScoreHelper.printHLVectorAsArray(vector) + "\n");
			if (parent != null) {
				Printer.print("parent: " + parent.toString());
			}

			InputStream is = new FileInputStream(ctx.getPureDigitalDataFilePath());
            int cnt = 1;
//			Rule.totalRuleNr = 0;
			//int ruleNr = 0;
			int ruleNrGlobal = 0;
			Rule r = null;
			Map<RuleComparable, SplitCluster> map = new HashMap<RuleComparable, SplitCluster>(); //<Integer, SplitCluster>
			do {
					r = ctx.getIOContext().readRuleFromPureDigital(is);
					//,ruleNr++, ruleNrGlobal++
					if (r != null) {
					r.ruleNrGlobal = ruleNrGlobal++;
					r.ruleNr = -1;
					boolean parentOk = true;
					if (parent != null) {
						parentOk = false;
						int siparent = Utils.printRuleAccordingToVectorDigital(
								r, parent.getVector());
						int siparentP = Utils.printRuleAccordingToVectorDigital(
								parent.getRule(), parent.getVector());
						
						
						if (siparent == siparentP) {
							parentOk = true;
						}
					}

					if (parentOk) {
						//int[] tmp = Utils.ruleValueFor(r, vector);
						//int si = Utils.printRuleAccordingToVectorDigital(r, vector);
						RuleComparable si = Utils.comparable(r, vector);
						SplitCluster c = (SplitCluster) map.get(si);
						if (c == null) {
							c = new SplitCluster(r, vector);
						}
						c.setCounter(c.getCounter() + 1);
						map.put(si, c);
						cnt++;

					}

				}
				if ((ctx.getRulesToLoad() > 0) && (cnt > ctx.getRulesToLoad())) {
					r = null;
				}
			} while (r != null);
			is.close();
			return map;

		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return null;
	}

}
